home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / oper_sys / quartz / quartz10.lha / doc / quartz.ms < prev    next >
Text File  |  1990-05-14  |  88KB  |  2,041 lines

  1. .    \" PT - page titles
  2. .de PT
  3. .lt \\n(LTu
  4. .pc %
  5. .nr PN \\n%
  6. .nr PT \\n%
  7. .if \\n(P1 .nr PT 2
  8. .if \\n(PT>1 .if !\\n(EH .if !\\n(OH .tl     
  9. .if \\n(PT>1 .if \\n(OH .if o .tl \\*(O1
  10. .if \\n(PT>1 .if \\n(EH .if e .tl \\*(E2
  11. .lt \\n(.lu
  12. ..
  13. .EQ
  14. delim $$
  15. .EN
  16. .nr LL 7.0i
  17. .nr PO 0.75i
  18. .nr PI 0.1i
  19. .nr HM 0.75i
  20. .nr FM 1.25i
  21. .ps 9
  22. .nr PS 9
  23. .vs 10
  24. .nr VS 10
  25. .LP
  26. .ce 1000
  27. .LG
  28. .LG
  29. \fBQuartz:  A Tool for Tuning Parallel Program Performance\fR 
  30. .sp 1.5
  31. .SM
  32. Thomas E. Anderson and Edward D. Lazowska
  33. .sp 0.5
  34. .SM
  35. Department of Computer Science and Engineering
  36. University of Washington
  37. Seattle WA  98195
  38. .sp 0.5
  39. September 1989
  40. .sp 2
  41. .LG
  42. \fBAbstract\fR
  43. .SM
  44. .ce 0
  45. .sp 0.5
  46. .PP
  47. .nh
  48. Initial implementations of parallel programs typically
  49. yield disappointing performance.
  50. Tuning to improve performance is thus a significant
  51. part of the parallel programming process.
  52. The effort required to tune a parallel program,
  53. and the level of performance that eventually is
  54. achieved, both depend heavily on the quality of
  55. the instrumentation that is available to the programmer.
  56. .PP
  57. This paper describes Quartz, a new tool for tuning parallel
  58. program performance on shared memory multiprocessors.
  59. The philosophy underlying Quartz was inspired
  60. by that of the sequential UNIX tool gprof:  to appropriately
  61. direct the attention of the programmer by efficiently measuring
  62. just those factors that are most responsible for
  63. performance and by relating these metrics to
  64. one another and to the structure of the program.
  65. This philosophy is even more important in the
  66. parallel domain than in the sequential domain,
  67. because of the dramatically greater number of
  68. possible metrics and the dramatically
  69. increased complexity of program structures.
  70. .PP
  71. The principal metric of Quartz is \fInormalized processor time\fR:
  72. the total processor time spent
  73. in each section of code divided by the number of other processors
  74. that are concurrently busy when that section of code is being executed.
  75. Tied to the logical structure of the program, this metric
  76. provides a "smoking gun" pointing towards those areas of the
  77. program most responsible for poor performance.  This information can be
  78. acquired efficiently by checkpointing to memory the number of busy
  79. processors and the state of each processor, and then statistically
  80. sampling these using a dedicated processor.
  81. .\" *** I think it was much clearer the way it was.
  82. .\" This information can be
  83. .\" acquired efficiently by checkpointing to memory the number of busy
  84. .\" processors and the state of each processor, and then statistically
  85. .\" sampling these using a dedicated processor.
  86. .\" Quartz computes a weighting factor for each procedure
  87. .\" by dividing processor time by concurrency
  88. .\" (summing over each element of the vector).
  89. .\" Quartz ties this metric to the logical structure of the program
  90. .\" by reporting it for each procedure and for
  91. .\" all of the activity performed on that procedure's behalf,
  92. .\" whether invoked synchronously via subroutine call or
  93. .\" asynchronously through parallelization.
  94. .\" The result is a "smoking gun" pointing towards those
  95. .\" areas most responsible for poor performance.
  96. .PP
  97. In addition to describing the design rationale,
  98. functionality, and implementation of Quartz, the paper
  99. examines how Quartz would be used to solve a number of
  100. performance problems that
  101. have been reported as being frequently encountered,
  102. and describes a case study in which Quartz was used
  103. to significantly improve the performance of a CAD circuit
  104. verifier.
  105. .sp
  106. .LP
  107. .nh
  108. \fIIndex Terms\fR \- multiprocessor, performance, measurement, 
  109. parallel programming, tuning
  110. .sp 2
  111. .MC 3.33i
  112. .FS
  113. .sp 0.5
  114. This material is based on work supported by
  115. the National Science Foundation
  116. (Grants No. CCR-8619663, CCR-8703049,
  117. and CCR-8700106),
  118. the Naval Ocean Systems Center,
  119. the Washington Technology Center,
  120. Digital Equipment Corporation (the Systems
  121. Research Center and the External Research Program),
  122. and IBM (a Graduate Fellowship).
  123. .sp
  124. Authors' address:  Department of Computer Science and Engineering FR-35,
  125. University of Washington, Seattle WA  98195; (206) 545-2675;
  126. \fItom/lazowska@cs.washington.edu\fR.  Quartz source code is
  127. available by anonymous ftp from cs.washington.edu.
  128. .sp 0.75i
  129. .FE
  130. .LP
  131. .NH
  132. Introduction
  133. .nr H1 1
  134. .nr H2 0
  135. .PP
  136. .nh
  137. The primary motivation behind building multiprocessors
  138. is to cost-effectively improve system performance.
  139. Even moderately increasing a
  140. uniprocessor's power can require substantial additional design effort
  141. as well as faster, and thus more expensive, hardware components.
  142. By contrast, once a scheme for interprocessor communication
  143. has been added to a uniprocessor design, the system's peak processing
  144. power can be increased linearly simply by adding processors.  The
  145. incremental cost per processor has been reported to
  146. be as little as 15% of the initial system cost for
  147. small to moderate numbers of processors [Thacker et al. 1988],
  148. and larger but still close to linear for greater numbers of processors
  149. [BBN 1985; Pfister et al. 1985].
  150. .PP
  151. Of course, multiprocessors lose their advantage
  152. if this processing power is not effectively utilized,
  153. and while it is relatively easy
  154. to get good performance when there are multiple independent sequential
  155. job streams, it can be difficult to achieve good performance from
  156. parallel applications.
  157. The literature describes many attempts to parallelize algorithms and
  158. applications.
  159. (Burkhart and Millen [1989] survey some of this
  160. experience.)\ 
  161. Typically, an initial implementation results in disappointing
  162. performance, but significant improvements can be obtained
  163. with subsequent effort.
  164. Sequent, for example, tells of a major customer whose first attempt
  165. at parallelizing a "dusty deck" resulted in a program that,
  166. given 8 processors,
  167. executed only 50% as fast as the original sequential program.  After
  168. considerable effort by skilled engineers, nearly perfect
  169. speedup (a factor of nearly 8 on an 8 processor machine) was achieved
  170. [Rodgers 1986].
  171. .PP
  172. A major factor contributing to the large amount
  173. of skilled effort typically required to achieve
  174. good parallel program performance is
  175. the shortage of good performance analysis
  176. tools.
  177. In the absence of such tools,
  178. performance problems must be identified through a
  179. combination of guesswork, folklore, and application-specific
  180. instrumentation.
  181. The subject of this paper is the design rationale,
  182. functionality, implementation, and use of a new
  183. tool for tuning parallel program performance.
  184. .PP
  185. The philosophy underlying our work is that
  186. an effective tool for tuning parallel program performance
  187. must be based
  188. on a clear view of the causes of poor performance,
  189. and on a specific methodology for improving that performance.
  190. By being selective about what it measures and presents, the tool can
  191. focus the programmer's attention on the information needed to tune
  192. performance, eliding details about second-order effects.
  193. Measurement efficiency also is improved by designing the tool to
  194. record just the important behavior.
  195. .PP
  196. Selectivity is possible because,
  197. although parallel performance in general is much more complex than
  198. sequential performance, experience (discussed in Section 3.2) suggests that
  199. poor parallel performance typically arises from a relatively small number
  200. of factors.  For applications whose performance is dominated
  201. by periods of limited parallelism,
  202. the tool should identify those sections
  203. of code that account for most of this time
  204. so that these sections can either be re-structured to increase concurrency
  205. or optimized to reduce their impact on overall performance.  Time
  206. spent spin- (or "busy"-) waiting must be correctly represented, since
  207. spinning processors appear
  208. to be busy even though they are not computing useful results.  Finally,
  209. for applications with large amounts of real parallelism, performance can
  210. only be improved by optimizing (but not further parallelizing) the code
  211. that executes for the greatest proportion of time.
  212. .PP
  213. Based on these observations, we
  214. propose a new way to view parallel program performance
  215. on shared memory multiprocessors.
  216. We focus on both the total processor time devoted to each
  217. section of code and the
  218. number of other processors concurrently busy (as opposed to idle or
  219. spin-waiting) when that section of code is being executed.
  220. Routines can be compared by considering their 
  221. \fInormalized processor time\fR:  their processor time divided 
  222. by the concurrent parallelism (a precise definition is given in Section 3).
  223. This metric usually reflects the relative importance of different
  224. sections of code to the overall elapsed time of the program:
  225. a routine that executes while no other processors are busy
  226. can be responsible for a large percentage of the runtime of a program,
  227. even though it uses only a small fraction of the total processor time.
  228. Further, by measuring both parallelism and processor time, we
  229. can determine whether performance can be improved
  230. by re-structuring to increase parallelism, or only by simple optimization.
  231. .PP
  232. We tie these measurements to the logical structure of the program's procedures.
  233. Good engineering practice demands that large programs,
  234. whether sequential or parallel, be structured using hierarchical abstractions
  235. [Graham et al. 1982].
  236. We report our performance measures for each procedure and for all the work
  237. done on its behalf, either synchronously via a normal procedure call or
  238. asynchronously through parallelization.
  239. The programmer can use this to walk through the hierarchy, focusing on
  240. just those procedures that, along with their children, account for most
  241. of the poor performance.
  242. We expect that for parallel programs as for sequential ones,
  243. a relatively small proportion of the code
  244. will be responsible for most of the runtime.
  245. .\" *** I find the following paragraph confusing at this point in
  246. .\" *** the paper, and I don't know how to fix it, so I've commeted
  247. .\" *** it out.  (I think this is ok to leave out -- tom)
  248. .\" .PP
  249. .\" We also propose a new type of program performance
  250. .\" measurement:  data-oriented instead of procedure-oriented.
  251. .\" We measure the breakdown of time spent on behalf of data objects,
  252. .\" since parallel performance can depend as much on the data object 
  253. .\" being manipulated as on the function being computed.
  254. .\" If a procedure is executed in parallel, performance can depend on 
  255. .\" the longest time it takes to execute on some piece of data,
  256. .\" rather than its average time; performance can also be limited
  257. .\" by the requirements of synchronizing accesses
  258. .\" to particular data objects.  To provide a single focus for the
  259. .\" programmer's attention, we
  260. .\" integrate data-oriented and function-oriented measurements into the 
  261. .\" same hierarchy defined by the program's logical structure.
  262. .PP
  263. These measurements can be made efficiently on a 
  264. shared memory multiprocessor by checkpointing to memory the number 
  265. of busy processors and
  266. the state of each processor, and then statistically
  267. sampling this information using a dedicated processor.
  268. .PP
  269. We have developed a tool
  270. to test these ideas, called \fIQuartz\fR.
  271. Quartz was built by modifying the application-level
  272. thread package described in [Anderson et al. 1989].
  273. Quartz uses
  274. only the normal profiling support available on UNIX-like shared memory 
  275. multiprocessors; it currently runs on the Sequent Symmetry multiprocessor 
  276. [Sequent 1988].
  277. .PP
  278. The remainder of the paper discusses these ideas in more detail.
  279. Section 2 examines existing measurement tools for tuning program performance.
  280. Section 3 describes Quartz:  its motivation, its functionality,
  281. its applicability to a number of performance problems that
  282. have been reported as being frequently encountered,
  283. and its implementation.
  284. Section 4 describes a case study in the use of Quartz
  285. to improve the performance of a specific parallel application,
  286. a CAD circuit verifier.
  287. Section 5 considers the implications of our work
  288. for the monitoring of sequential programs
  289. and non-shared-memory multiprocessors.  Section 6 summarizes our results.
  290. .NH
  291. Existing Tools for Tuning Program Performance
  292. .NH 2
  293. Tools for Sequential Programs
  294. .PP
  295. .nh
  296. The philosophy underlying Quartz owes much to
  297. the experience of UNIX gprof [Graham et al. 1982],
  298. a tool for tuning the performance of sequential
  299. programs running on uniprocessors.
  300. .PP
  301. Years of experience tuning sequential programs indicate
  302. that the major difficulty is focus:  it
  303. is relatively easy for the programmer to improve the processing
  304. time of a small section of code, but lots of effort is
  305. commonly wasted in the wrong places \- tweaking code that has
  306. only a small impact on overall performance.
  307. .PP
  308. Gprof's solution is to highlight the "hot spots" of the
  309. program, and to do so in a way that exploits the
  310. hierarchical structure
  311. of large programs.
  312. Gprof presents to the programmer the total processor time of
  313. each procedure, including time spent on its behalf if it calls other routines.
  314. With this information, the programmer
  315. can tune the program in a top-down fashion,
  316. focusing effort on those functions that have the greatest
  317. impact on performance.
  318. .PP
  319. Gprof is relatively efficient.  It periodically interrupts the program
  320. to sample the program counter, thereby estimating the execution time
  321. of each procedure.  While sampling produces only an estimate,
  322. the approach is most accurate just where it needs to be:  for
  323. those routines where the program spends most of its time.
  324. Gprof also collects the call graph:  who called whom how many times.
  325. This is done by using compiler support that makes each procedure execute
  326. a monitoring routine
  327. during its prologue.  Gprof then computes its central metric, the
  328. processor time spent on a procedure's behalf, by making the
  329. assumption that all calls to the same procedure take the same amount of time.
  330. .\" *** this don't add much anymore
  331. .\" (i.e., that processor time is not call-source-dependent).
  332. Processing time is propagated bottom-up from callee to caller according to
  333. the caller's proportion of the total calls.
  334. .PP
  335. Gprof seems so natural in retrospect that it is easy
  336. to forget the alternative approach taken by a number of
  337. other tools:  to (expensively) measure everything that could
  338. conceivably be of relevance to program performance,
  339. and to report these measurements without concern for how
  340. they relate to each other or to the structure of the program.
  341. .PP
  342. Our goal for Quartz was to achieve
  343. a tool for tuning parallel program performance
  344. that is analogous to gprof in that it
  345. efficiently measures exactly what is important,
  346. and relates these measurements to one another
  347. and to the structure of the program.
  348. This philosophy is even more important in the
  349. parallel domain than in the sequential domain,
  350. because of the dramatically greater number of
  351. performance metrics and the dramatically
  352. increased complexity of program structures.
  353. The next two sub-sections discuss, in this
  354. context, existing approaches to tools for
  355. tuning parallel program performance.
  356. .NH 2
  357. Non-Integrated Tools for Parallel Programs
  358. .PP
  359. Many useful measures of parallel program performance
  360. have been proposed.
  361. Each provides a view of some important aspect of program behavior.
  362. However, in many existing tools, their lack of integration with each other
  363. and with program structure limits their usefulness.
  364. .PP
  365. Perhaps the simplest approach to parallel program measurement
  366. is to extend sequential UNIX gprof to run on a multiprocessor.
  367. In place of processor time on one processor, multiprocessor gprof 
  368. measures the sum
  369. of the time spent on each processor [Aral & Gertner 1988].
  370. .\" *** I didn't see that this was a major point; reinstate it if
  371. .\" *** you disagree.  (its clearer now using processing time)
  372. .\" (Note that at any given moment, each processor may be executing
  373. .\" a different procedure.)\ 
  374. Unfortunately, as we have noted, a procedure's total processor time is not
  375. related in a simple way to parallel runtime.  
  376. .\" Given two procedures with the
  377. .\" same processor time, a procedure that always executes when
  378. .\" all other processors are idle,
  379. .\" such as an initialization routine, will have a disproportionate impact
  380. .\" on performance compared to a routine that always runs when other
  381. .\" processors are busy.
  382. Something more than a straightforward adaptation of
  383. sequential UNIX gprof clearly is necessary.
  384. .PP
  385. Another common tool displays the number of busy processors across time
  386. by periodically sampling the number of runnable processes.
  387. Assuming that all activity is due to the
  388. program in question, this allows the programmer to see if there are periods
  389. of time when there is too little parallelism to keep all the processors busy
  390. [Halstead 1986].
  391. A significant shortcoming of tools like this is that
  392. it can be difficult to relate the periods of poor parallelism
  393. to specific sections of code that can be changed.
  394. Further, the fact that some processors are not doing useful work can be concealed,
  395. since spin-waiting processors appear to be busy.
  396. .PP
  397. The DEC SRC Firefly has a tool that measures the time spent
  398. waiting for each lock protecting a shared
  399. data structure [Thacker et al. 1988].
  400. A lock ensures that threads manipulating
  401. the shared data structure
  402. have mutually exclusive access to it.  This serial execution
  403. can limit performance.  By measuring the wait time, the tool can
  404. determine which critical sections
  405. are the worst bottlenecks; these can then be re-structured to increase
  406. parallelism.  This information is useful, but long waits for a lock
  407. will not affect performance if there is always other work to do
  408. during the wait,
  409. and monitoring a lock
  410. can increase the length of time that the lock is held,
  411. creating "artificial bottlenecks" when monitoring is
  412. enabled.
  413. .PP
  414. Quartz provides many of the same metrics as these tools, but
  415. correlates the metrics to one another and to the structure of the program.
  416. For example, Quartz measures not only
  417. how many processors are busy, but also which procedures execute
  418. during periods of low and high parallelism.
  419. .NH 2
  420. Trace-Based Tools for Parallel Programs
  421. .PP
  422. The issue of determining in advance exactly what information
  423. will be needed to tune the performance of a parallel program
  424. can be finessed by recording
  425. a trace of every interprocessor synchronization event
  426. with a timestamp of when the event occurred.
  427. The behavior
  428. of the program can be completely reconstructed from such a trace
  429. [Fowler et al. 1988]. \(dg
  430. .FS
  431. \(dg Originally, Fowler et al. implemented trace collection to aid
  432. debugging the correctness of parallel programs; they then showed
  433. that the same support could be used for program tuning.  An implication
  434. of our work is that the support needed for correctness debugging is not
  435. necessary for performance tuning.
  436. .FE
  437. Arbitrary metrics (whether general or application-specific) can be
  438. computed using a common
  439. interface to the trace data.
  440. Finally, the metrics obtained from the trace can be
  441. integrated with each other and with the structure
  442. of the program.
  443. .PP
  444. One drawback to this approach is that both
  445. the collection and the post-processing of
  446. trace files is expensive.
  447. For programs that perform frequent synchronization, the
  448. trace files can be prohibitively large.  Consider
  449. a program running on a hypothetical shared memory multiprocessor 
  450. with 20 5-MIPS processors, each of which on average performs a 
  451. monitorable event (such as acquiring or releasing a spin lock)
  452. every 500 instructions, and where each event 
  453. record is 10 bytes.  If the program being monitored executes for one 
  454. minute, the trace file will be 100 megabytes.
  455. Similar estimates appear in [Malony et al. 1989]
  456. to justify hardware support for recording traces.
  457. .\" *** I know I asked for it, but I don't like it!
  458. .\" (While traces
  459. .\" may be needed for debugging the correctness of a parallel program,
  460. .\" those traces are likely to be much smaller than those needed for
  461. .\" tuning performance, by using fewer processors on smaller data sets.)
  462. .\" *** OK, this is gone too!
  463. .\" Carpenter [1987] proposes hardware support
  464. .\" for collecting traces on an Intel iPSC/1; the amount of physical
  465. .\" memory in the system is \fIdoubled\fR so that trace data can be stored
  466. .\" locally to avoid distorting the performance behavior being measured.
  467. .\" *** Seemed somewhat redundant.  I'm trying to reduce the amount
  468. .\" *** of text, and this seems expendable.  Again, restore if you
  469. .\" *** strongly disagree. (no, it is.)
  470. .\" .PP
  471. .\" Collecting a complete trace postpones the problem of deciding 
  472. .\" what is important.
  473. .\" Since a parallel program can be viewed as pieces of sequential
  474. .\" code connected by communication events [Hoare 1978], it might seem
  475. .\" that in order to understand parallel performance,
  476. .\" we would need to know the pattern of communication in time
  477. .\" This pattern of events can be graphically presented to the programmer,
  478. .\" with processors or processes along one axis and time along the other,
  479. .\" along with the ability to zoom in on areas that are performance 
  480. .\" bottlenecks.  As programs become more complex, however,
  481. .\" the programmer easily becomes lost, since there is no obvious connection 
  482. .\" from the visual pattern of events
  483. .\" to overall performance or to the specific sections of code that require 
  484. .\" improvement.
  485. .\" (The authors last year wrote
  486. .\" just such a system, with the predictable results.)
  487. .PP
  488. Nevertheless, tools have been developed that collect trace
  489. data and post-process it into useful measures.  We argue later
  490. that the much of the information provided by these tools can
  491. be measured or approximated more efficiently by sampling.
  492. .PP
  493. Monit [Kerola & Schwetman 1987], for example, uses a trace file to
  494. compute the behavior of higher-level objects, such as the number
  495. of busy processors or the number of threads waiting to enter
  496. each critical section.  The behavior of each object is then
  497. plotted on a timeline.  After identifying those phases of execution
  498. with few busy processors, the programmer can visually correlate these
  499. phases to the behavior of other objects (discovering, for example,
  500. that parallelism is low while a specific critical
  501. section has a large number of waiting threads).
  502. .PP
  503. Although Monit's display is at a higher level than the raw trace data,
  504. it still can present a massive amount of data to the programmer.
  505. Only a few timelines
  506. will fit on a screen at a time, but Monit provides little help
  507. in identifying those containing
  508. information relevant to the measured lack of
  509. parallelism.
  510. As the complexity of the application increases, so does the number of objects
  511. to monitor, making focusing more important.
  512. .PP
  513. IPS [Miller & Yang 1987] attempts both to guide the programmer to performance
  514. problems and to provide useful statistics about those problems.
  515. Its central focusing metric is the time each process and procedure
  516. spends executing the critical path.
  517. The critical path is the longest path through the task graph \- the series of
  518. sequential pieces of code (that cannot be done in parallel because
  519. they communicate one to the next) that takes the longest to execute.
  520. By definition, the elapsed execution time of the program can be 
  521. reduced only by shortening the length of the critical path.
  522. .PP
  523. One drawback to critical path analysis is its expense:  it requires
  524. a complete trace of all interprocessor communication.
  525. (To be fair, IPS was originally designed to measure
  526. programs running on local area networks of uniprocessors.  Because of the high
  527. latency and low bandwidth communication on these systems,
  528. only programs with relatively infrequent synchronization
  529. .\" very coarse-grained parallelism
  530. can run efficiently.
  531. Under these conditions, the size of the trace file would be manageable.)\ 
  532. Yet critical path analysis is still just a heuristic:  there
  533. .\" (as is our focusing metric):  there
  534. is no guarantee that reducing the critical path will actually
  535. reduce the execution time of the program.  There may be another
  536. path through the task graph with almost the same length that will be
  537. unaffected by the change.
  538. .\" *** Seems unnecessary. (right)
  539. .\" The existence of this other path
  540. .\" is related to the width of the task graph (the parallelism) 
  541. .\" where the improvement is made.  Improving sequential code will always 
  542. .\" improve performance; improving code that always runs when there are
  543. .\" other busy processors may not, since those other processors are 
  544. .\" likely computing needed results.
  545. .\" .PP
  546. Critical path analysis also does not indicate
  547. \fIhow\fR to reduce a procedure's completion time.  One way is
  548. to reduce its sequential execution time.
  549. Another is to parallelize it.
  550. But parallelization will only be of benefit if there are idle processors
  551. to exploit when the procedure runs.
  552. .\" When used by itself, critical path analysis has some limitations. (For
  553. .\" this reason, IPS also computes some other measures of program behavior.)
  554. .NH
  555. Quartz:  Its Functionality, Applicability, and Implementation
  556. .PP
  557. .nh
  558. Our goals for a tool for tuning
  559. parallel program performance are:
  560. .IP \-
  561. It should identify the sections of source code most
  562. responsible for poor performance.
  563. .IP \-
  564. It should present
  565. its measurements hierarchically, to allow top-down tuning
  566. according to the logical structure of the program.
  567. .IP \-
  568. It should measure parallelism (properly representing spin-waiting
  569. as a loss of parallelism) and it should tie this to the source code,
  570. identifying where re-structuring to increase parallelism is
  571. necessary and where code optimization is appropriate.
  572. .IP \-
  573. It should measure program behavior in sufficient detail to
  574. provide some insight into the type of re-structuring that
  575. will work.
  576. .IP \-
  577. It should do all this efficiently and without significantly
  578. affecting the behavior of the measured program.
  579. .PP
  580. Quartz meets these criteria.  Before describing it, we
  581. must define some terms.  A thread (or "lightweight process")
  582. is a sequential execution stream; it is the basic unit of
  583. parallel work.
  584. A thread starts another thread by giving it a procedure to run;
  585. the initial thread continues in parallel with the created thread.
  586. Thread creation is thus essentially an asynchronous procedure call.
  587. If threads are implemented as part of an application library,
  588. they can be within an order of magnitude of the cost of a procedure
  589. call [Anderson et al. 1989]; they can thus be used for procedure-level
  590. parallelism.  
  591. .PP
  592. Threads can synchronize with one another.  One type of synchronization
  593. object is a lock, used to ensure mutually exclusive
  594. access to a shared data structure.  Another is a
  595. condition or barrier used to enforce a data dependency, as when one
  596. thread reads data produced by some other thread.
  597. In both cases, synchronization may cause
  598. the thread to wait, either because the lock is busy
  599. or because the data it requires has not been
  600. produced.  Since there may be more threads than processors,
  601. a waiting thread has a choice:  either spin
  602. until the lock is free or the data is available, or block,
  603. relinquishing the processor to run another thread.
  604. Thus, there is a difference between a program's \fIeffective\fR parallelism,
  605. the number of busy (not idle or spinning) processors, and its \fInominal\fR
  606. parallelism, the number of runnable threads, some of which may be spinning.
  607. Our measurements refer to the activity of just the processors executing
  608. the application, and not any processors concurrently executing other applications.
  609. .\" As a rule of thumb, the thread should spin if
  610. .\" the expected wait time is shorter than twice the time to do a scheduling
  611. .\" operation, or if there is no other work to do.
  612. .\" .PP
  613. .\" Two key terms in the discussion that follows are \fIeffective
  614. .\" parallelism\fR and \fInominal parallelism\fR.
  615. .\" By effective parallelism we mean
  616. .\" the number of busy (not idle or spinning) processors.
  617. .\" By nominal
  618. .\" parallelism we mean the number of runnable threads, some of which may
  619. .\" be spin-waiting.
  620. .\" *** NOTE:  I'm not really sure that deleting this is OK.
  621. .\" .PP
  622. .\" Finally, we note that our discussion assumes
  623. .\" that the application is assigned a
  624. .\" fixed number of processors.  Our measurements refer to the activity
  625. .\" of just those processors, and not any processors concurrently executing
  626. .\" other applications.
  627. .\" It is a straightforward
  628. .\" implementation issue to accommodate multiprogrammed multiprocessors
  629. .\" with processor reassignment, but the discussion is simplified by
  630. .\" ignoring this.
  631. .PP
  632. The remainder of this section is divided into three
  633. sub-sections.
  634. The first describes the functionality of Quartz:  the specific
  635. metrics that it reports.
  636. The second shows how these metrics can be used to detect
  637. .\" demonstrates the applicability of Quartz by showing how it can be used to detect
  638. and fix a number
  639. of performance problems that have been identified by
  640. others as commonly occurring.
  641. The third provides an overview of the implementation of
  642. Quartz.
  643. A case study in which Quartz was used to tune a specific application
  644. is described in Section 4.
  645. .NH 2
  646. The Functionality of Quartz
  647. .\" .PP
  648. .\" In this sub-section, we outline Quartz's functionality, discuss
  649. .\" how this functionality can be used to solve common performance problems,
  650. .\" delaying until afterwards a description of its implementation
  651. .\" on a shared memory multiprocessor.
  652. .PP
  653. The principal measurement made by Quartz is normalized processor time,
  654. defined in Equation 1, where $P$ is the number of processors.
  655. Measuring this weighting function for every procedure allows us to 
  656. compare them according to their effect on overall performance.
  657. .KF
  658. .sp 0.5
  659. .EQ
  660. sum from {i = 1} to {P}~{{Processor~time~with~i~processors~concurrently~busy} over {i}}
  661. .EN
  662. .sp 0.3
  663. .ce 5
  664. \fBEquation 1:  Normalized Processor Time\fR
  665. .ce 0
  666. .sp 0.5
  667. .KE
  668. .\" each procedure's total processor time,
  669. .\" subdivided according to the distribution of the effective parallelism
  670. .\" while it is executing.
  671. .\" Quartz computes a weighting factor for each procedure
  672. .\" by dividing each level of effective parallelism
  673. .\" into the corresponding processor time value, and summing
  674. .\" these ratios.
  675. .\" ***** Ed's old attempt
  676. .\" To understand the rationale for this weighting factor,
  677. .\" consider a program that initializes its data structures
  678. .\" sequentially and then computes its result completely in parallel.
  679. .\" As a first example, assume
  680. .\" that the initialization and the computation take the same total
  681. .\" processor time.
  682. .\" Then the initialization will require a factor of $N$ greater
  683. .\" elapsed time (where $N$ is the number of processors) and
  684. .\" will have a much greater impact on performance than the
  685. .\" computation.
  686. .\" As a second example, assume that the initialization and
  687. .\" the computation take the same total elapsed time.
  688. .\" Then improvements in either will have equal impact on
  689. .\" performance.
  690. .PP
  691. To understand the rationale for this metric,
  692. consider a program with two functions, one that always executes 
  693. sequentially when no other processors are busy and one that computes its result 
  694. completely in parallel.  If each function takes the same total
  695. processor time, the sequential one requires a factor of $P$ greater
  696. elapsed time (where $P$ is the number of processors) and
  697. will have a much greater impact on program performance.
  698. If the two functions take the same elapsed time,
  699. then the same percentage improvements in either will have equal 
  700. impact on performance.
  701. .PP
  702. Further, knowing the concurrent effective parallelism while
  703. a routine executes is more important than knowing the effective parallelism 
  704. it generates:  a serial routine that is always overlapped with other computation 
  705. will have little effect on performance compared to a serial routine that
  706. always executes by itself.  This is despite the fact that the
  707. \fIelapsed time\fR of the two functions is the same.
  708. .\" *** The following is correct but confusing, and probably unnecessary.
  709. .\" A different way of viewing this weighting function is that we take the
  710. .\" time spent by idle or spinning processors, assign it to the procedures
  711. .\" that were executing when they were idle, and then normalize by the
  712. .\" number of processors.
  713. .PP
  714. Normalized processor time reflects these observations.
  715. Quartz also measures other program behavior; the principal performance 
  716. measurements in Quartz are summarized in Table 1.
  717. .KF
  718. .sp 0.5
  719. .TS
  720. center box;
  721. l.
  722. T{
  723. .in -1.5i
  724. .ll +1.5i
  725. .nr LL +1.5i
  726. .LP
  727. .nh
  728. For each procedure, synchronization object, 
  729. and thread, and for the work done on its behalf:
  730. .IP
  731. Normalized processor time.
  732. Each term of the sum is measured separately.
  733. .IP
  734. Elapsed time spent in each state (busy, spinning, blocked, or ready),
  735. along with the average and the distribution of the number of 
  736. runnable threads while it is in each state.
  737. .sp 0.3
  738. .in +1.5i
  739. .ll -1.5i
  740. .nr LL -1.5i
  741. T}
  742. .sp 0.5
  743. .TE
  744. .\" .in -1i
  745. .\" .ll +1i
  746. .\" .nr LL +1i
  747. .\" .vs +1
  748. .\" .nr VS +1
  749. .ce 5
  750. \fBTable 1:  Principal Performance Measurements in Quartz\fR
  751. .ce 0
  752. .sp 0.5
  753. .KE
  754. .PP
  755. To focus the programmer's attention on
  756. those areas of the program that have the greatest
  757. impact on performance, we sort procedures by their
  758. normalized processor time plus that of the work done
  759. on their behalf.
  760. This includes work done synchronously or asynchronously 
  761. (via threads).  The program's top-level procedure, then, has a normalized 
  762. processor time equal to the elapsed time of the program; the functions 
  763. it calls to do the work of the program divide that weight among them.
  764. Quartz's ordering is analogous to what gprof does 
  765. with processor time, except that Quartz uses a weighting function
  766. related to parallel performance.  In both Quartz and gprof,
  767. the programmer can trace performance top-down through the program.
  768. .PP
  769. Normalized processor time indicates \fIwhere\fR improvements can
  770. be made.
  771. Quartz also provides information about \fIwhat\fR can be
  772. done to improve performance.  Part of this information
  773. comes from the measurement
  774. of concurrent parallelism needed for normalized processor time.
  775. This indicates whether performance can be improved by
  776. re-structuring to increase effective parallelism,
  777. or only by simple optimization.
  778. Certainly, procedures that always execute when all processors are busy 
  779. will not benefit from further parallelism.
  780. .PP
  781. Given that re-structuring is necessary, an accounting of
  782. the \fIelapsed time\fR spent by a procedure can help identify 
  783. what kind of re-structuring is most likely to succeed.
  784. Quartz measures each procedure's elapsed time spent busy, spinning,
  785. blocked, or ready to run, along with the average and the distribution of 
  786. the number of threads that are available to run while the procedure 
  787. is in each state.
  788. For example, if a procedure is busy executing and there are
  789. few other busy processors, then the nominal parallelism indicates
  790. whether the other processors are idle or spinning.  If idle,
  791. then performance can be improved by parallelizing the 
  792. procedure (creating threads to do its work),
  793. provided this is possible.  If spinning, then there is
  794. no benefit to creating more threads.
  795. Similarly, threads that are blocked or spinning represent deferred work;
  796. if the program were re-structured to reduce or eliminate waiting,
  797. then parallelism would increase.
  798. .PP
  799. Quartz makes the above measurements separately for each procedure,
  800. synchronization 
  801. object, and thread.  Measurement based purely on procedures would ignore
  802. the fact that parallel performance can depend more on the
  803. data object passed to a procedure than on the implementation of the procedure
  804. itself.  It is only mildly interesting to know the total time spent 
  805. waiting at all barriers; it is much more useful to know that some specific
  806. barrier accounted for most of that time.  As a special case,
  807. the time spent executing in a critical section is attributed to
  808. the lock on that critical section.
  809. (Quartz also measures queue length distributions for synchronization
  810. objects.)\ 
  811. Per-thread information allows us to determine if different threads
  812. executing the same procedure (on different data objects) have
  813. different performance.
  814. .PP
  815. By measuring synchronization objects and threads in the
  816. same way as procedures, we can present the programmer a uniform focusing
  817. metric.  All are ranked in the same way 
  818. to simplify tracing performance through the program.  For example,
  819. if contention for a lock determines performance,
  820. the lock object will have a high normalized processor time since its 
  821. critical section is always executing while few other processors are.
  822. (Spin time is factored out in computing normalized processor time.)
  823. .PP
  824. An important difference from gprof is that we explicitly
  825. measure the work done on behalf of a procedure or lock object.
  826. Gprof explicitly measures only the work done within
  827. each procedure, making the assumption that the processor time
  828. of its children is independent of who called them.  Gprof uses the
  829. call graph (who called whom, and how many times)
  830. to propagate processor times from callee to caller
  831. according to the caller's percentage of the total calls to the callee.
  832. We cannot make a similar assumption.  The effective and nominal parallelism 
  833. while a procedure executes depend not only on what that procedure does,
  834. .\" (whether or not it creates parallelism),
  835. but also on the parallelism when it is called.
  836. Different calls to a low-level formatting routine might have
  837. vastly different concurrent parallelism.  Still, even though it is not
  838. useful for propagating
  839. our measurements, Quartz does record the call graph
  840. (including calls to/from synchronization objects) to help
  841. the programmer in tracing performance top-down through the program.
  842. .NH 2
  843. Detecting Frequently Encountered Performance Problems Using Quartz
  844. .PP
  845. In this section, we argue by example that Quartz is useful for detecting and
  846. fixing a number of common parallel program performance problems.
  847. We asserted in Section 1 that
  848. although parallel performance in general is much more complex than
  849. sequential performance, experience suggests that
  850. poor parallel performance typically arises from a relatively small number
  851. of factors.
  852. One piece of evidence for this is Table 2, which
  853. lists the performance problems most frequently
  854. encountered by three "vendors" of parallel computing systems
  855. who participated in a working group concerned with 
  856. "Sources of Performance Degradation" at the \fINSF/CMU Workshop
  857. on Performance-Efficient Parallel Programming\fR in 1986.
  858. .\" The systems represent a range of architectural styles and
  859. .\" parallel programming methodologies.  Sequent and Harris
  860. .\" represent shared memory multiprocessors, while Warp processors are 
  861. .\" connected in a mesh, with high-bandwidth interprocessor
  862. .\" communication instead of shared memory.
  863. The key observation is that
  864. none of the problems involve subtle timing issues that might require
  865. a complete trace of synchronization activity.
  866. .KF
  867. .\" .in +1i
  868. .\" .ll -1i
  869. .\" .nr LL -1i
  870. .\" .vs -1
  871. .\" .nr VS -1
  872. .sp 0.5
  873. .TS
  874. center box;
  875. l.
  876. T{
  877. .in -1.5i
  878. .ll +1.5i
  879. .nr LL +1.5i
  880. .LP
  881. .nh
  882. Sequent
  883. .RS
  884. .IP 1.
  885. A problem decomposition that puts most of the
  886. work in one thread (e.g., the optimizing phase
  887. of a concurrent compiler or a "busy" region
  888. in a ray-tracing algorithm), so that little
  889. real concurrency can be realized.
  890. .IP 2.
  891. Memory thrashing due to a poor choice of operating
  892. system parameters.
  893. .IP 3.
  894. Excessive I/O that is not overlapped with computation.
  895. .IP 4.
  896. A synchronous software structure, such as might
  897. arise from a very large granularity, a producer-consumer
  898. relationship with a small number of buffers, or the
  899. use of an unnecessarily restrictive synchronization
  900. construct (e.g., barriers where critical sections
  901. would suffice).
  902. .RE
  903. .sp 0.5
  904. .LP
  905. Harris
  906. .RS
  907. .IP 1.
  908. Synchronization overhead.
  909. .IP 2.
  910. Contention for shared variables, including counting
  911. semaphores, task queues, and the "problem heap".
  912. .IP 3.
  913. Starvation due to a small problem size.
  914. .RE
  915. .sp 0.5
  916. .LP
  917. Warp
  918. .RS
  919. .IP 1.
  920. Excessive I/O that is not overlapped with computation.
  921. .IP 2.
  922. Data dependencies in loops.
  923. .RE
  924. .in +1.5i
  925. .ll -1.5i
  926. .nr LL -1.5i
  927. T}
  928. .sp 0.5
  929. .TE
  930. .\" .in -1i
  931. .\" .ll +1i
  932. .\" .nr LL +1i
  933. .\" .vs +1
  934. .\" .nr VS +1
  935. .ce 5
  936. \fBTable 2:  Frequently Encountered Performance Problems\fR
  937. (\fINSF/CMU Workshop on Performance-Efficient 
  938. Parallel Programming\fR)
  939. .ce 0
  940. .sp 0.5
  941. .KE
  942. .PP
  943. The first issue in tuning any parallel program is
  944. to identify which segments are responsible for the poor performance.
  945. As with sequential programs, we would expect that of the large number
  946. of functions in a parallel
  947. program, a relative few will be responsible for
  948. most of the program's runtime.  By computing a weight based on both
  949. processor time and parallelism, and by accounting for all of the activity
  950. done on behalf of a function, Quartz allows the programmer to walk
  951. through the program hierarchy to find those few functions.
  952. Once the general area of difficulty has been located, the
  953. approach to tuning depends on the situation:
  954. .NH 3
  955. Functional Decomposition
  956. .PP
  957. Some computations have several functionally
  958. distinct parts, each assigned to a distinct processor.
  959. An example of this is a pipelined compiler:  separate threads
  960. (and processors) execute
  961. the scanner, parser, optimizer, and code generator,
  962. streaming
  963. results one to the next (Sequent #1 in Table 2).  Performance
  964. difficulties usually relate to load imbalance.
  965. If one phase has more work to do than the others, the
  966. others must sit idle waiting for it.  If the optimizer is the bottleneck,
  967. the scanner and parser will have to wait for buffer space to forward their
  968. partial results, while the code generator must also periodically wait
  969. for results to be completed.
  970. .PP
  971. Quartz would identify this problem:  the thread executing
  972. the optimizer would have a longer execution time, spend more time
  973. executing when few other processors are busy, and thus have a larger
  974. normalized processor time than the threads executing the other phases.
  975. Other tools would also handle this case.  For example,
  976. a display of processor activity across time would show that the processor
  977. executing the optimizer was always busy, while the other processors
  978. sometimes wait.
  979. (Of course, many tools that display processor activity fail to
  980. relate processors to procedures.)\ 
  981. Similarly, a critical path analysis would show that the
  982. execution of the optimizer constituted most of the critical path.
  983. .PP
  984. It is also easy with Quartz to identify the phase that is the
  985. secondary bottleneck \- in the compiler example, the one that would limit
  986. performance if the optimizer were improved.
  987. If the code generator ran for almost as long
  988. as the optimizer, then it would have slightly less normalized processor time,
  989. indicating that attention should be focused on improving
  990. both phases.  It is difficult to extract this information
  991. from a timeline,
  992. since all phases but the optimizer periodically block.
  993. Critical path analysis only identifies the primary
  994. bottleneck, so iteration would be required.
  995. .PP
  996. Another performance problem with pipelines is starvation (Harris #3).
  997. This occurs if the problem size is small relative to the time for each phase
  998. to start streaming results.
  999. In this case, the later phases spend much of the
  1000. total time waiting to start; the earlier phases finish well before
  1001. the program completes.
  1002. Quartz would show that
  1003. each phase spends much of its \fIelapsed time\fR idle.  (Normalized
  1004. processor time would highlight the first and last stages, since their work is
  1005. the least overlapped with other stages.)  A solution is to reduce
  1006. the time before each phase starts streaming its first results.
  1007. .NH 3
  1008. Data Decomposition
  1009. .PP
  1010. Some programs compute the same function
  1011. on many pieces of data.
  1012. These programs can be parallelized by assigning
  1013. different pieces of data to different processors.  Unlike functional
  1014. decomposition, each processor executes the same function at the same time.
  1015. Again, a frequent issue is load balancing:  the
  1016. required computation
  1017. may vary widely for different pieces of data.  An example of this
  1018. is ray-tracing
  1019. where part of the picture has the majority of the activity
  1020. (Sequent #1);
  1021. another is a fluid dynamics computation where turbulence is concentrated
  1022. in certain regions.
  1023. In such situations, performance is limited by the processor assigned
  1024. to the data regions with the longest execution times.
  1025. .PP
  1026. Whether a different thread is used to run the function on each object,
  1027. or on collections of objects, Quartz will show if the
  1028. execution times are balanced.  If they are not,
  1029. one of the threads will execute while other processors are idle,
  1030. and there will be long average queue lengths at the barrier
  1031. that checks that all threads have completed before continuing.
  1032. .PP
  1033. It can be difficult to relate the
  1034. performance of a thread to the symbolic names
  1035. of the data objects it works on,
  1036. particularly in conventional (non-object-oriented)
  1037. languages.  For instance, the procedure a thread is to execute can
  1038. be passed an index that only implicitly refers to the object it is to work on.
  1039. As a result, we rely on the programmer to make this connection by providing
  1040. a symbolic name when each thread is created.
  1041. In an object-oriented language such as C++ we could extend Quartz
  1042. not only to keep track of the symbolic names of data objects passed to
  1043. threads, but also to explicitly take measurements for each
  1044. procedure-data object pair, to allow
  1045. both an object-oriented and a procedure-oriented view of performance.
  1046. We intend to port Quartz to Presto [Bershad et al. 1988], a C++ based
  1047. parallel programming system, to further explore this topic.
  1048. .\" .PP
  1049. .\" Another problem that can arise with data decomposition is data dependency.
  1050. .\" Effective parallelism can be limited if the computation on one piece of
  1051. .\" data depends on the result of another.  For instance, each loop iteration
  1052. .\" can use a result computed in the previous iteration (Warp #2).
  1053. .\" Initially there is lots of available parallelism, but soon each
  1054. .\" blocks, and the computation proceeds sequentially or near-sequentially.
  1055. .\" Whether a thread is used for each iteration, or a thread running on
  1056. .\" each processor grabs iterations explicitly, there will be long
  1057. .\" queue lengths at the point where the data dependency is enforced;
  1058. .\" these will be detected by Quartz.
  1059. .NH 3
  1060. Synchronization
  1061. .PP
  1062. The need to synchronize the work of different processors
  1063. can cause another class of performance problems.  For instance,
  1064. execution time is increased by the overhead of parallelizing the
  1065. job:  distributing work to various processors, serializing
  1066. access to shared data structures, and enforcing data dependencies
  1067. (Harris #1).
  1068. Even if the program is perfectly parallel, this time can dominate.
  1069. Fortunately, it is easy to measure.
  1070. If there is a sequential version of the program,
  1071. many of its functions will correspond to equivalent functions in the 
  1072. parallel version, and the execution time of each function 
  1073. can be directly compared to
  1074. determine the effect of overhead.  (The
  1075. execution time added by monitoring must of course be factored out.)
  1076. Alternately, given measurements
  1077. of the performance of the thread package, the number of calls to
  1078. each thread function, such as to create a thread or to acquire a lock,
  1079. can be used to compute overhead.
  1080. .PP
  1081. Performance can also be affected by waiting for data dependencies
  1082. to be satisfied (Warp #2; Sequent #4) or for access to a busy
  1083. critical section (Harris #2).
  1084. Waiting threads represent
  1085. deferred parallelism; Quartz identifies this by measuring queue lengths 
  1086. and the average wait time (the total elapsed time spent waiting divided
  1087. by the number of accesses to that synchronization object).
  1088. For example, if a loop data dependency limits
  1089. parallelism, there will be a long queue length
  1090. at the point where the data dependency is enforced.
  1091. Note that two of the examples of contention cited in Table 2 are
  1092. for locks within the thread package; we measure contention for these
  1093. locks in the same way that we measure locks in the application code.
  1094. .PP
  1095. Even if there are many threads waiting on a synchronization object,
  1096. the question of
  1097. whether it makes sense to re-structure the program
  1098. to release that parallelism depends on whether the time
  1099. is spent spinning or blocked, and on the nominal parallelism.
  1100. When there are at least as many runnable threads as processors, 
  1101. blocked threads have no 
  1102. impact on performance beyond the initial context switch.  Re-structuring
  1103. to increase the number of ready threads does not help in this case.
  1104. By contrast, spin-waiting always wastes processing cycles,
  1105. regardless of the number of runnable threads, but if there are excess runnable
  1106. threads
  1107. .\" and the average time spent spinning is longer than the twice the
  1108. .\" context switch time
  1109. then performance could be improved by blocking instead
  1110. of spinning.
  1111. .PP
  1112. If re-structuring is necessary, the number of threads waiting at 
  1113. a lock can be decreased by any
  1114. of:  reducing the number of accesses (from the call graph), thereby
  1115. reducing contention; decreasing the size of the critical section
  1116. (its busy time); distributing accesses more evenly across time
  1117. (if the queue length is sometimes zero and sometimes very long);
  1118. or modifying the protected data structure to allow parallel accesses
  1119. (for example, by giving each processor a separate copy).
  1120. .PP
  1121. Waiting for data dependencies can be reduced by computing the 
  1122. data earlier, or, if an overly restrictive synchronization construct was 
  1123. used, by allowing the thread to continue temporarily without it. 
  1124. Fuzzy barriers are a special case of the latter [Gupta 1989].
  1125. .NH 3
  1126. Input/Output
  1127. .PP
  1128. The time spent doing I/O was mentioned twice
  1129. in Table 2 (Sequent #2, Warp #1).  If a program reads a significant amount
  1130. of data from an I/O device, then the reads should be overlapped with the
  1131. computation; in other words,
  1132. the reads should be started early so that they complete before
  1133. the data is needed.
  1134. The natural style, however, is synchronous:  when the data is referenced,
  1135. start a disk read and wait until it returns.
  1136. .PP
  1137. As a result of the operating system interface on the Sequent,
  1138. the current implementation of Quartz measures time spent 
  1139. doing I/O as processor time, attributed
  1140. to the procedure that performs the I/O.  If the I/O
  1141. is not overlapped, the relative importance of the time spent waiting 
  1142. in the kernel will be increased because
  1143. processors will be idle waiting for the I/O to finish.
  1144. Given kernel support for
  1145. threads, Quartz could monitor the kernel disk queue as a 
  1146. normal synchronization object.
  1147. .PP
  1148. If a program spends a lot of time doing disk accesses, it may benefit from
  1149. exploiting parallelism in the disk sub-system.  Tuning
  1150. a program's use of parallel disks is in many ways similar to tuning
  1151. its use of parallel processors, although initial file placement is an
  1152. issue as well.  We expect that some of the techniques we have
  1153. described in this paper could be applied to this problem.
  1154. .NH 3
  1155. Limitations
  1156. .PP
  1157. We have designed Quartz to measure only those aspects of program
  1158. behavior that are needed to detect and fix frequently occurring
  1159. parallel performance problems.  The tradeoff is that Quartz therefore
  1160. does not help with every performance problem that can occur in parallel
  1161. applications.
  1162. .PP
  1163. When threads execute at the same time, Quartz
  1164. weights each equally even though only one is on the critical path.
  1165. As an example, consider a program with a critical section that
  1166. restricts parallelism.
  1167. The processor time spent executing outside of the critical section can
  1168. appear important, because there are few other processors concurrently
  1169. executing, even though reducing or parallelizing it will have no effect on
  1170. program's performance.
  1171. Although this can seem anomalous, Quartz's metric can help in this 
  1172. case by identifying code that may be a secondary bottleneck.
  1173. We are currently investigating ways of augmenting Quartz's measurements
  1174. to address this limitation.  For instance, each time a processor goes idle,
  1175. we could measure how long it stays idle, and then add that time to
  1176. processor time of the code that causes the processor to become busy.
  1177. This metric correctly handles busy critical sections 
  1178. (the time spent waiting for the lock would be attributed to the 
  1179. critical section), but it does worse than normalized processor time in other
  1180. situations.
  1181. .PP
  1182. Quartz also does not measure thread scheduling decisions (although problems
  1183. can sometimes be identified, for instance, if a thread spends a long
  1184. time waiting for a processor and then executes serially)
  1185. or contention for the bus or memory, even though these can affect performance.
  1186. .\" *** Dumped!
  1187. .\" on program
  1188. .\" performance.  The assignment of threads to processors is often under the 
  1189. .\" control of the application, either because threads are implemented as
  1190. .\" an application-level library, or because the operating system kernel 
  1191. .\" provides 
  1192. .\" this support.  Different assignments can result in different performance.
  1193. .\" For example, threads that frequently inter-communicate
  1194. .\" should be co-scheduled to avoid context switches [Ousterhout 1984],
  1195. .\" but Quartz does not measure whether two threads did or did not
  1196. .\" run concurrently.
  1197. .\" .PP
  1198. .\" However, the behavior of the scheduler, and its effect on performance,
  1199. .\" depends on thread execution times and the structure of communication 
  1200. .\" between threads.  These both change as the program is tuned.
  1201. .\" The schedule also depends on the number of processors given to
  1202. .\" the application.  Thus, tuning the thread schedule is likely to
  1203. .\" be done last, if at all.
  1204. .\" .PP
  1205. .\" Similarly, bus contention (or memory contention in systems with
  1206. .\" multistage interconnection networks) affects parallel performance on 
  1207. .\" shared memory multiprocessors.  Again, this issue is likely
  1208. .\" to become important only once the program is already fairly well 
  1209. .\" parallelized,
  1210. .\" since only then would many processors be actively accessing the bus.
  1211. .\" Measuring the program to help tune this is a problem, however.
  1212. .\" It is relatively simple to build a hardware monitor to measure
  1213. .\" total bus utilization, but this does not indicate which sections
  1214. .\" of code should be changed to reduce the utilization.  Rather,
  1215. .\" if this becomes important, as we think it will eventually,
  1216. .\" we would need a tool that collects a memory-reference trace
  1217. .\" (at least of shared variables), processes the trace to determine
  1218. .\" which references would result in bus traffic,
  1219. .\" and ties the bus traffic back to particular data structures.
  1220. .\" While cache misses that would occur on a uniprocessor probably
  1221. .\" cannot be optimized, bus traffic that communicates results
  1222. .\" between processors perhaps can be.
  1223. .\" Weber and Gupta [1989], for instance, have measured several different
  1224. .\" shared variable access patterns that have widely different performance
  1225. .\" under invalidation-based cache coherency.
  1226. .NH 2
  1227. The Implementation of Quartz
  1228. .PP
  1229. We have implemented Quartz on a Sequent Symmetry shared memory
  1230. multiprocessor [Sequent 1988].  The Sequent runs DYNIX, a multiprocessor 
  1231. adaptation of UNIX.  Since DYNIX processes are too expensive to use
  1232. directly as threads, we built our system by adding monitoring code to 
  1233. the thread package described in [Anderson et al. 1989].
  1234. That thread package works by creating a DYNIX process for each processor,
  1235. and then multiplexing threads onto the DYNIX processes.
  1236. Our implementation did not modify DYNIX or the C compiler;
  1237. it used only the support they provide for gprof.
  1238. .PP
  1239. Our implementation addresses the twin concerns of
  1240. efficiency and accuracy.  Because program tuning is 
  1241. iterative and interactive, a tool's usability depends on the
  1242. elapsed time from program compilation
  1243. to report production.
  1244. Accuracy is trickier.  Unlike sequential
  1245. programs where the execution overhead due to monitoring is easily
  1246. factored out, a change to a parallel program can alter its behavior
  1247. in subtle ways.  For instance, monitoring code that increases the
  1248. time that a lock is held may increase the contention for the lock.
  1249. Analogously, instrumentation added outside of a critical section
  1250. will cause a net decrease in the contention for that critical
  1251. section.
  1252. .PP
  1253. Our approach is to use statistical sampling by a dedicated processor.
  1254. A set of processors executes the program normally, maintaining
  1255. their state in shared memory by special code
  1256. executed during thread operations
  1257. and at procedure entry and exit.  This state is then 
  1258. sampled by a dedicated processor that does not participate in
  1259. executing the program.
  1260. We impose no synchronization beyond hardware interlocks between
  1261. the sampling processor and the other processors; rarely-accessed
  1262. locks are used by the normally executing
  1263. processors in building the call graph.
  1264. .PP
  1265. We sample by means of a dedicated processor
  1266. rather than interrupts because interrupts
  1267. cannot provide accurate correlations between processor
  1268. state and overall program state. 
  1269. On the Sequent, as with most multiprocessors, interrupts are fielded 
  1270. by each processor asynchronously; by the time the program state is
  1271. sampled, it may have changed in a way affected by the fact that
  1272. there was an interrupt.  For example, if the interrupted processor
  1273. is holding a lock, the queue length at the lock will be greater
  1274. than a purely random sample would indicate.
  1275. Similarly, measuring a procedure's execution time directly with timestamps 
  1276. does not allow us to correlate that time to the number of busy processors.
  1277. Of course, although it reduces the effect of monitoring on the measured 
  1278. program, sampling by a dedicated processor does not eliminate distortion.
  1279. Updating state adds time to the computation, and even the 
  1280. recording of samples by the dedicated processor can increase bus and
  1281. cache coherence traffic, thereby slowing other processors.
  1282. .\" *** The implementation section is too long.  Rather than crafting
  1283. .\" *** it, I'm using a hatchet.
  1284. .\" .PP
  1285. .\" Another approach would be to generate a timestamped trace
  1286. .\" of all procedure calls and interprocessor synchronization events,
  1287. .\" and, to avoid having to save the trace to a file, concurrently process
  1288. .\" it to compute the useful metrics.  Such a trace would contain all of 
  1289. .\" the information needed for the measurements we describe above. 
  1290. .\" A similar approach is used by Parasight to collect normal gprof data 
  1291. .\" [Aral & Gertner 1988].
  1292. .\" Unfortunately, this approach imposes severe real-time constraints.
  1293. .\" It is difficult to make even an estimate of the rate at which events
  1294. .\" will be generated; thus, it would be hard to know how many
  1295. .\" extra processors would be needed to prevent buffers of trace data
  1296. .\" from overflowing.  Further, the more processors that must be
  1297. .\" dedicated to making measurements, the less applicable are those
  1298. .\" measurements to the program's performance without monitoring,
  1299. .\" when it is given all of the system's processors.
  1300. .\" .PP
  1301. .\" Instead, in our implementation, the processors executing the program 
  1302. .\" maintain state in shared memory that is statistically sampled from 
  1303. .\" a dedicated processor.  If the sampling rate achieved by a single
  1304. .\" processor is too low, it can
  1305. .\" be increased linearly by adding processors.
  1306. .\" In a multiprogrammed environment, the dedicated processor must stop
  1307. .\" sampling a processor when it is pre-empted from the application. 
  1308. .\" This can be done either implicitly, if all processors, including
  1309. .\" the dedicated processor, are pre-empted together, or explicitly
  1310. .\" by being told by the operating system when a pre-emption occurs.  
  1311. .\" (Tucker and Gupta [1989] have shown severe performance degradation
  1312. .\" can occur if applications are not informed when some of their processors
  1313. .\" are pre-empted.)
  1314. .PP
  1315. The nominal and effective parallelism are
  1316. maintained in centralized counters, updated when
  1317. a thread is added to the ready queue and when a processor
  1318. becomes idle or starts or stops spinning.
  1319. The counters are maintained with atomic increment and decrement instructions,
  1320. to avoid making access to them a bottleneck.  Most multiprocessors,
  1321. including the Sequent, support such instructions.
  1322. .PP
  1323. In addition to the execution stack, we maintain a profile stack
  1324. of monitored procedures for each thread.
  1325. This allows us to record both the time spent in a procedure
  1326. and the time spent on behalf of the procedure.
  1327. The dedicated processor copies the number of
  1328. busy and ready threads, copies the profile stack,
  1329. and then bumps the appropriate
  1330. measurement record for each different procedure on the stack.
  1331. (Recursive procedures are counted only once.)
  1332. While the stack may have changed
  1333. between recording the number of busy threads and copying the stack,
  1334. reducing consistency, sampling itself is only an approximation.
  1335. In particular, we do not lock the profile stack to prevent changes 
  1336. from occurring;
  1337. this would unnecessarily perturb the execution of the program.
  1338. Note that locking would be harder to avoid if we were to sample directly
  1339. from the execution stack since that would require tracing the chain of 
  1340. frame pointers.
  1341. .PP
  1342. The profile stacks are of fixed size, established at compile time.
  1343. Overflows are caught, prevented, and later reported to the programmer.
  1344. We expect that overflows will occur only rarely, since we
  1345. push a procedure onto the stack only if the previous entry
  1346. is different, eliminating immediately recursive calls,
  1347. the most common cause of arbitrarily deep stacks.
  1348. This also has the effect of reducing the work of
  1349. the sampling processor.
  1350. .PP
  1351. We use only the normal compiler support provided for gprof.
  1352. A monitoring routine is called in the prologue of each profiled procedure.
  1353. Exactly as gprof does, we use this routine to update the count of calls
  1354. to the procedure from its caller; we also push the procedure onto
  1355. the profile stack.  Because the compiler inserts only a prologue call, we
  1356. manipulate the execution stack so that when the procedure returns, it returns
  1357. first to our code that pops the profile stack, and then to the
  1358. caller procedure.  This is a bit inefficient, but easy to implement.
  1359. .PP
  1360. To simplify mapping from the entries on the profile stack 
  1361. to the measurement
  1362. data for each procedure, we assign each procedure a unique ID.
  1363. The gprof monitoring routine is passed a pointer to a procedure-specific
  1364. location; this was originally used to count the number of calls to
  1365. the procedure.  After the program has been linked, we modify the
  1366. object file so that each
  1367. procedure's location holds a unique ID; this ID is what is pushed
  1368. onto the stack and used to index the procedure's data record.
  1369. Gprof, by contrast, uses the address in the program counter to index the 
  1370. data record for a procedure; this requires space proportional to
  1371. the size of the code segment.  By using procedure IDs, Quartz requires 
  1372. space proportional to the number of procedures times the number of processors.
  1373. .PP
  1374. The synchronization routines in our thread library are specially modified.
  1375. Each object has a data record containing the call graph and execution
  1376. time information.  When the object is accessed, the call graph is updated
  1377. and a pointer to the synchronization object is pushed; the
  1378. pointer is popped when the thread no longer must wait.
  1379. Locks are handled as a special case.
  1380. Normally, the procedure that acquires a lock is the one that releases
  1381. it, in which case we are safe to push the lock before it is accessed
  1382. and pop it after it is released.  This attributes the time
  1383. spent waiting for and holding the lock
  1384. to the lock object, and only adds two instructions to the inside
  1385. of the critical section:  setting the
  1386. state of the thread to no longer spinning,
  1387. and incrementing the number of busy processors.
  1388. .PP
  1389. When a thread is created, we copy the profile stack from the creating thread
  1390. to the new thread.  This allows the sampling processor to attribute 
  1391. execution time across asynchronous procedure calls.
  1392. .\" A pointer to the thread-specific data record is also pushed.
  1393. .PP
  1394. Quartz has two other useful features.
  1395. More than one processor can be dedicated to sampling to improve measurement
  1396. accuracy and resolution.  Quartz automatically removes from its measurements
  1397. most of the time spent by the normally executing processors in updating 
  1398. their monitored state.
  1399. .PP
  1400. Our system does not currently provide for interactive control of
  1401. which routines are to be profiled.
  1402. This would be easy to add,
  1403. .\" *** Again, I'm hatcheting what I consider to be detail.  I admit
  1404. .\" *** that at this point I'm not using much subtlety.
  1405. .\" A bit could be associated with
  1406. .\" each procedure ID, synchronization
  1407. .\" object, or thread to turn profiling on or off
  1408. .\" during execution.  Note that by using a separate stack, we correctly
  1409. .\" attribute execution time spent in procedures that are not profiled
  1410. .\" to the procedures that called them that were profiled.  Because gprof
  1411. .\" uses the call graph to propagate execution time, and no call graph data 
  1412. .\" is recorded for non-profiled procedures, it cannot do this.
  1413. .\" .PP
  1414. but in truth, we doubt that it is the right approach.
  1415. Aral and Gertner [1988] argue that
  1416. gprof's overhead is too high to allow only compile-time control.
  1417. They use this to motivate Parasight, a system for execution-time
  1418. code modification and re-linking.  But the
  1419. overhead of gprof, and of our system, could be dramatically reduced
  1420. with a small amount of compiler support.
  1421. For example, most of the time in gprof
  1422. is spent building the call graph; it crawls up the execution stack to find
  1423. the caller address, hashes on it, checks the callee address, etc.
  1424. A simpler method is to determine caller-callee pairs at compile time
  1425. and to simply bump a statically allocated counter before each call.
  1426. Calls made via function pointers, a rarer case, could
  1427. use the current, slower approach.
  1428. .NH
  1429. A Case Study:  Using Quartz to Tune a CAD Circuit Verifier
  1430. .PP
  1431. We argued "abstractly" in Section 3.2 that Quartz
  1432. is well-suited to detecting and fixing
  1433. a spectrum of parallel program performance
  1434. problems that have been identified by others as commonly occurring.
  1435. Of course, the crucial question is whether Quartz
  1436. is an effective tool in practice.
  1437. In this section, we describe our experience in using Quartz 
  1438. to tune an existing parallel application.
  1439. .PP
  1440. The application we tuned, called Verify [Ma et al. 1987], compares 
  1441. two different circuit implementations to determine whether they are 
  1442. functionally (Boolean) equivalent.  It was written for a
  1443. dissertation to demonstrate that an existing production 
  1444. CAD program could be parallelized with good speedup.
  1445. The program has 2900 lines of C code, and was written for 
  1446. a Sequent Balance with twelve processors.
  1447. The circuits we used as inputs in our tests
  1448. were combinational benchmarks for evaluating test generation algorithms.
  1449. .PP
  1450. The initial speedup of Verify
  1451. on our Sequent Symmetry was already good:  9.2 using 18 processors.
  1452. (Because no sequential version of the program was available,
  1453. speedup was measured as the time to run the program, including
  1454. process creation and I/O, on one processor divided by the
  1455. time to run it on 18 processors.)
  1456. Even though neither of us was familiar with the program or with
  1457. CAD algorithms in general, over the course of several hours
  1458. we were able to improve its performance by 40%.
  1459. Its initial runtime was 114.4 seconds on one processor and 12.4 seconds on 
  1460. 18; with our changes, the runtime dropped to 7.7 seconds on 18 processors.
  1461. Most of this improvement came within the first few minutes of using
  1462. Quartz, demonstrating the utility of using normalized processor time
  1463. as a weighting function.
  1464. .PP
  1465. Figure 1 shows a portion of the Quartz output when run on the
  1466. initial version of Verify.  After the data has been collected,
  1467. Quartz can interactively draw a graph (on an X Window display)
  1468. of the total normalized
  1469. processor time of any monitored procedure or synchronization object
  1470. as a function of the number of concurrently busy processors.
  1471. Normalized processor time is based on each routine's 
  1472. processor time divided by its concurrent parallelism,
  1473. whether the parallelism is due to that routine or to other 
  1474. activity in the program.  The top-level routine "main", however,
  1475. is responsible for all of the activity in the program;
  1476. its graph of normalized processor time is equivalent to the 
  1477. elapsed time the program spent with each number 
  1478. of busy processors.
  1479. Different shadings highlight the role of the routine itself, 
  1480. and its children, in the routine's total normalized processor time.
  1481. .KF
  1482. .sp 0.5
  1483. .ft C
  1484. .ce 2
  1485. Procedure: main
  1486. .br
  1487. Normalized processor time: 16.36 sec. (100%)
  1488. .sp 2.5i
  1489. .br
  1490. .TS
  1491. center;
  1492. l c s c
  1493. l c s c
  1494. l n n n.
  1495. Name    Normalized    Calls
  1496.     processor time
  1497. _
  1498. .sp 0.29
  1499. work    10.43    (64%)    18
  1500. .sp 0.29
  1501. create_cone    3.62    (22%)    2
  1502. .sp 0.29
  1503. input    1.93    (12%)    2
  1504. .sp 0.29
  1505. main self    .31    ( 2%)
  1506. .TE
  1507. .sp 0.5
  1508. .ft P
  1509. .ce 5
  1510. \fBFigure 1:  Initial Quartz Output for Verify "main"\fR
  1511. .ce 0
  1512. .sp 0.5
  1513. .KE
  1514. .PP
  1515. The Quartz output for Verify shows that although most of the program
  1516. is indeed highly parallel, a significant portion of its runtime
  1517. is spent executing serial code.  Further, Quartz identifies
  1518. "create_cone" as being responsible for most of this serial execution time.
  1519. By examining the graph for "create_cone", and in turn the graph for
  1520. its child with the largest normalized processor time, we found that
  1521. the program was spending a quarter of its time executing print routines
  1522. in order to log a trace of shared data structures as they were created.
  1523. .PP
  1524. While Quartz easily identified this performance problem, gprof would not
  1525. have.  The logging routines accounted for less than 2% of
  1526. the program's sequential processor time, but because they occurred during the 
  1527. serial initialization phase of the program, they accounted for
  1528. a much larger share of the parallel performance.
  1529. Before Quartz pointed it out, we did not know that the program 
  1530. was even doing logging.
  1531. .PP
  1532. Quartz allowed us to make an informed choice about a performance 
  1533. tradeoff:  we could substantially improve performance by removing logging,
  1534. or if this functionality was central to the program, at least we would
  1535. know by how much it reduced performance.  Hypothesizing that it was not
  1536. important, we made logging a command line option.  With logging turned
  1537. off, the program's parallel performance improved by close to the
  1538. amount predicted by Quartz, while its serial performance improved only
  1539. slightly.  As a result, the program's speedup improved from 9.2 to 12.2.
  1540. .PP
  1541. When we re-profiled the modified program, Quartz showed that
  1542. a significant fraction of the program's runtime was still spent in 
  1543. the sequential initialization phase.
  1544. To reduce this, we read in and allocated data structures for the two input
  1545. circuits in parallel with each other and with the operating system
  1546. process creation needed to start the program running on all 18 processors.
  1547. This improved performance somewhat to a speedup of 12.9.
  1548. .PP
  1549. At this point, we stopped trying to further parallelize the program.
  1550. Once a program's speedup is high, further improvements
  1551. become much more difficult.  The routines responsible for the difference
  1552. from ideal speedup account for only a small fraction of the total
  1553. program runtime; thus even radical improvements to these routines
  1554. can reduce overall runtime only slightly.
  1555. .PP
  1556. In our case, Quartz showed that virtually all of the program's runtime was
  1557. now being spent executing entirely in parallel.  The remaining
  1558. time was split between processor starvation during initialization and
  1559. termination.  During initialization, Quartz showed that performance
  1560. was limited by the fact that the input files were not balanced (one circuit
  1561. was larger and therefore took longer to read in than the other).
  1562. During termination, the problem was that some processors finished early 
  1563. and had to wait for the rest to finish; dividing the problem into smaller 
  1564. size sub-problems might help this problem.  Fixing these problems seemed 
  1565. to be more effort than it was worth.
  1566. .PP
  1567. Instead, we noted that small changes in the routines that
  1568. account for most of the parallel execution time
  1569. would have a large relative effect on runtime.  According to Quartz, two
  1570. routines accounted for over half the execution time of the modified program,
  1571. and we were able to improve performance somewhat with
  1572. a few quick tweaks to these routines.  (These routines were already
  1573. highly tuned from the original sequential program.)
  1574. .PP
  1575. Table 3 summarizes the changes that we made to Verify and their
  1576. effects on sequential and parallel performance.  Note that our
  1577. last improvement in fact decreased the program's speedup.
  1578. By reducing the execution time of the parallel portion of the
  1579. program, we increase the relative importance of the program's
  1580. sequential component.
  1581. .KF
  1582. .sp 0.5
  1583. .TS
  1584. center box;
  1585. c c s c
  1586. c c c c
  1587. l n n n.
  1588. Version    Elapsed Time (sec.)    Speedup
  1589.     Serial    Parallel
  1590. _
  1591. Original    114.4    12.4    9.2
  1592. Without logging    109.7    9.0    12.2
  1593. Parallel input    109.7    8.5    12.9
  1594. Serial optimization    92.3    7.6    12.2
  1595. .TE
  1596. .sp 0.5
  1597. .ce 5
  1598. \fBTable 3:  Performance Effect of Changes to Verify\fR
  1599. .ce 0
  1600. .sp 0.5
  1601. .KE
  1602. .PP
  1603. A major motivation behind Quartz is efficiency.
  1604. We measured
  1605. the overhead Quartz added to this application.
  1606. Quartz increased the elapsed runtime of Verify by roughly
  1607. the same amount as gprof:  about 70%.  (While Quartz is able
  1608. to remove most of this overhead from its measurements, some overhead
  1609. does appear in the graph in Figure 1.)  Quartz is as fast as gprof
  1610. because even though Quartz does more work on each procedure call
  1611. and synchronization event, it need not periodically interrupt 
  1612. (and thereby slow) the execution of the program, because a 
  1613. dedicated processor is used for sampling instead.
  1614. Verify makes stringent demands on Quartz:  it
  1615. generates roughly 9 million procedural and synchronization events
  1616. (roughly 1 million per second when running on 18 processors).
  1617. Even with 18 processors running, a single dedicated
  1618. sampling processor was able to sample each processor's
  1619. activity every 6 milliseconds, faster than gprof's sampling rate on DYNIX.
  1620. .PP
  1621. Something that we did not
  1622. expect was demonstrated by using Quartz on a real
  1623. application:  there is less "performance locality" in parallel programs
  1624. than in sequential ones.  The top eight procedures account for 95%
  1625. of the elapsed time of Verify on one processor.
  1626. With 18 processors, though, it
  1627. takes over 20 procedures to reach the same 95% level.
  1628. In retrospect, the reason is obvious.  The routines that account
  1629. for most of the time on one processor are parallelized and therefore
  1630. account for much less of the program's runtime on multiple processors;
  1631. at the same time, routines that take only a small amount of processor time
  1632. can become important if they run sequentially.
  1633. .NH
  1634. Implications for Other Systems
  1635. .PP
  1636. While we have implemented Quartz on a shared memory multiprocessor,
  1637. our work has implications for other systems.
  1638. .PP
  1639. On multiprocessors with distributed memory, such as the Intel Hypercube,
  1640. a dedicated sampling processor would not have efficient access
  1641. to the state of other processors.  Explicit messages would have to be used
  1642. to update the counts of effective and nominal parallelism, as well
  1643. as the procedures each processor was executing.
  1644. A further problem is that
  1645. programs on these systems are often explicitly written
  1646. to use a specific number of processors because of the need to explicitly
  1647. control the communication pattern; removing one for sampling
  1648. might require re-writing the program.
  1649. .PP
  1650. Alternative approaches also have significant drawbacks on
  1651. such systems.
  1652. In particular,
  1653. recording and post-processing a complete trace may already be 
  1654. impractical for some programs,
  1655. and will become more so as distributed memory multiprocessors support
  1656. faster rates of interprocessor communication.
  1657. .PP
  1658. Efficient sampling could be implemented given
  1659. hardware support for stopping all processors at close to the same time
  1660. (i.e., by allowing a host computer to send parallel interrupts each processor).
  1661. One of the reasons for using a dedicated processor is that
  1662. interrupting any single processor to do sampling can distort
  1663. the behavior being measured.  If all were stopped together,
  1664. the sample could be taken from that snapshot
  1665. without measurement error.  The sampling could be implemented
  1666. efficiently by using the processing power of the stopped processors.
  1667. .PP
  1668. Absent hardware support, it may be possible to exploit the
  1669. characteristics of parallel programs on distributed memory
  1670. multiprocessors.  Because of the requirement that interprocessor
  1671. communication be explicitly programmed using messages,
  1672. these systems are most commonly used for highly data-parallel
  1673. applications with regular communication patterns.  For these types
  1674. of programs, at any point during the computation, each processor
  1675. executes roughly the same section of code, although one may finish
  1676. before another.  As a result, sampling the behavior of each individual
  1677. processor, and not the global state, may yield a sufficiently detailed
  1678. picture of program performance.
  1679. .PP
  1680. The techniques used in
  1681. Quartz also solve some problems with traditional approaches
  1682. to tuning sequential program performance.
  1683. One limitation to gprof is that it cannot be used to tune
  1684. the implementation of operating system kernel-level routines,
  1685. since interrupt-driven sampling cannot measure code that runs
  1686. with interrupts disabled.  By using a separate processor to do sampling,
  1687. however, we would be able to accurately measure kernel-level execution.
  1688. .\" The kernel routines would update the profile stack.
  1689. .\" If the stack is made readable to application-level programs,
  1690. .\" the sampling routines need not even run in the kernel.
  1691. Note that by avoiding synchronization between the executing processors,
  1692. or between them and the sampling processor, the processor in the kernel
  1693. can execute the profiling code even if it holds the low level scheduler lock.
  1694. .PP
  1695. Similarly, by using a profile stack for sampling, we are able 
  1696. to account correctly for execution time spent out of the monitored
  1697. address space.
  1698. Because of the way gprof propagates execution time spent on behalf of
  1699. a procedure, it cannot accurately attribute time spent executing in
  1700. non-profiled code, or in the operating system on behalf of the program.
  1701. However, a trend in the design of operating systems and large applications
  1702. is to decompose them into separate modules in different address
  1703. spaces, so that hardware protection mechanisms can be used
  1704. for failure isolation.  Much of the execution time of an editor,
  1705. for instance, might be spent in another address space responsible for
  1706. updating the display.
  1707. The performance of the entire decomposed system could be measured
  1708. by sampling profile stacks shared among all address spaces.
  1709. .\" sampling could focus on a single address space by attributing time
  1710. .\" spent in other address spaces to the procedure that invoked that activity.
  1711. .PP
  1712. Data-oriented measurement can be helpful for tuning sequential programs
  1713. as well as parallel programs.
  1714. .\" A trend in software engineering
  1715. .\" is toward object-oriented programming.
  1716. For some programs,
  1717. knowing that a particular procedure accounts for a large
  1718. proportion of the processing time may not be as useful
  1719. as knowing that a particular object
  1720. is expensive.  For example, there is better graphics resolution in a more
  1721. finely tiled sphere, but it also costs more to draw.
  1722. Gprof introduces a systematic bias in measuring
  1723. object-oriented program behavior because
  1724. it assumes that a procedure call
  1725. always takes the same amount of time to execute (i.e., that the time
  1726. does not depend on the data that is passed).
  1727. Quartz avoids this bias by propagating execution times explicitly.
  1728. While we currently make only limited measurements of data-oriented
  1729. performance behavior,
  1730. our design is extensible to a more thorough object-oriented implementation.
  1731. .NH
  1732. Conclusions 
  1733. .PP
  1734. Achieving good performance from parallel applications
  1735. is both crucial and challenging.
  1736. We have discussed the design rationale,
  1737. functionality, implementation, and use of Quartz,
  1738. a tool for tuning parallel program performance
  1739. on shared memory multiprocessors.
  1740. .PP
  1741. The philosophy underlying our work is that
  1742. an effective tool for tuning parallel program performance
  1743. must be based
  1744. on a clear view of the causes of poor performance,
  1745. and on a specific methodology for improving that performance.
  1746. By being selective about what it measures and presents, Quartz can
  1747. focus the programmer's attention on the information needed to tune
  1748. performance.  Measurement efficiency also results from designing the tool to
  1749. record just the important behavior.
  1750. .PP
  1751. By relating the execution
  1752. of sections of code and the use of certain data objects with the
  1753. concurrent behavior of other processors, Quartz
  1754. assists in identifying areas of the program where re-structuring
  1755. is necessary to improve performance, and in gaining insight
  1756. into the types of re-structuring that will work.  Because Quartz organizes
  1757. performance information according to the logical structure of the program,
  1758. the programmer can tune performance in a top-down fashion.
  1759. .SH
  1760. Acknowledgments
  1761. .PP
  1762. We would like to thank Hi-Keung Tony Ma for donating the application
  1763. program we used for our case study, and Brian Bershad, Henry Levy,
  1764. and John Zahorjan for their extensive comments.
  1765. .SH
  1766. References
  1767. .\" .nr PS 8
  1768. .\" .ps 8
  1769. .\" .nr VS 9
  1770. .\" .vs 9
  1771. .nh
  1772. .LP
  1773. [Anderson et al. 1989]
  1774. .RS
  1775. Thomas E. Anderson, Edward D. Lazowska, and Henry M. Levy.
  1776. The Performance Implications of Thread Management Alternatives
  1777. for Shared Memory Multiprocessors.
  1778. \fIIEEE Transactions on Computers 38\fR,12 (December 1989), pp. 1631-1644.
  1779. .\" \fI1989 ACM SIGMETRICS and Performance '89
  1780. .\" Conference on Measurement and Modeling of Computer Systems\fR,
  1781. .\" pp. 49-60, May 1989.  To appear,
  1782. .RE
  1783. .LP
  1784. [Aral & Gertner 1988]
  1785. .RS
  1786. Ziya Aral and Ilya Gertner.
  1787. Non-Intrusive and Interactive Profiling in Parasight.
  1788. \fIProc. ACM/SIGPLAN PPEALS 1988\fR, pp. 21-30.
  1789. .RE
  1790. .LP
  1791. [BBN 1985]
  1792. .RS
  1793. BBN Laboratories.  Butterfly Parallel Processor Overview.  1985.
  1794. .RE
  1795. .\" .LP
  1796. .\" [Bershad 1988]
  1797. .\" .RS
  1798. .\" Brian Bershad.
  1799. .\" Thrips:  A Performance Debugging Tool for Parallel Programs.
  1800. .\" Department of Computer Science, University of Washington, 1988.
  1801. .\" .RE
  1802. .LP
  1803. [Bershad et al. 1988]
  1804. .RS
  1805. Brian Bershad, Edward Lazowska, and Henry Levy.
  1806. Presto:  A System for Object-Oriented Parallel Programming.
  1807. \fISoftware:  Practice
  1808. and Experience 18\fR,8 (Aug. 1988), pp. 713-732.
  1809. .RE
  1810. .LP
  1811. [Burkhart & Millen 1989]
  1812. .RS
  1813. H. Burkhart and R. Millen.
  1814. Performance Measurement Tools in a Multiprocessor Environment.
  1815. \fIIEEE Transactions on Computers 38\fR,5 (May 1989), pp. 725-737.
  1816. .RE
  1817. .LP
  1818. [Carpenter 1987]
  1819. .RS
  1820. R.J. Carpenter.  Performance Measurement Instrumentation for Multiprocessor
  1821. Systems.  In \fIHigh Performance Computer Systems\fR, ed. E. Gelenbe, 
  1822. North-Holland, pp. 81-92, 1987.
  1823. .RE
  1824. .\" .LP
  1825. .\" [Couch 1988]
  1826. .\" .RS
  1827. .\" A.L. Couch.
  1828. .\" Graphical Representations of Program Performance on Hypercube 
  1829. .\" Message-Passing Multiprocessors.
  1830. .\" Ph.D. Thesis, Department of Computer Science, Tufts University, April 1988.
  1831. .\" .RE
  1832. .\" .LP
  1833. .\" [Davis & Hennessy, 1988]
  1834. .\" .RS
  1835. .\" Helen Davis and John Hennessy.
  1836. .\" Characterizing the Synchronization Behavior of Parallel Programs.
  1837. .\" \fIProc. ACM/SIGPLAN PPEALS 1988\fR, pp. 198-211.
  1838. .\" .RE
  1839. .\" .LP
  1840. .\" [Dritz & Boyle 1987]
  1841. .\" .RS
  1842. .\" Kenneth W. Dritz and James M. Boyle.
  1843. .\" Beyond "Speedup":  Performance Analysis
  1844. .\" of Parallel Programs.
  1845. .\" Technical Report ANL-87-7, Mathematics and
  1846. .\" Computer Science Division, Argonne National Laboratory,
  1847. .\" Feb. 1987.
  1848. .\" .RE
  1849. .LP
  1850. [Fowler et al. 1988]
  1851. .RS
  1852. Robert J. Fowler, Thomas J. LeBlanc, and
  1853. John M. Mellor-Crummey.
  1854. An Integrated Approach to Parallel Program
  1855. Debugging and Performance Analysis on
  1856. Large-Scale Multiprocessors.
  1857. \fIProc. ACM SIGPLAN/SIGOPS Workshop on
  1858. Parallel and Distributed Debugging\fR,
  1859. May 1988.
  1860. .RE
  1861. .LP
  1862. [Graham et al. 1982]
  1863. .RS
  1864. S.L. Graham, P.B. Kessler, and M.K. McKusick.
  1865. Gprof:  A Call Graph Execution Profiler.
  1866. \fIProc. ACM SIGPLAN Symposium on Compiler
  1867. Construction\fR, June 1982.
  1868. .RE
  1869. .\" .LP
  1870. .\" [Gregoretti & Segall 1985]
  1871. .\" .RS
  1872. .\" F. Gregoretti and Z. Segall.
  1873. .\" Programming for Observability Support in a Parallel Programming Environment.
  1874. .\" Department of Computer Science, Carnegie Mellon University, 1985.
  1875. .\" .RE
  1876. .LP
  1877. [Gupta 1989]
  1878. .RS
  1879. R. Gupta.
  1880. The Fuzzy Barrier:  A Mechanism for High Speed Synchronization of Processors.
  1881. \fIProc. 3rd International Conference on Architectural
  1882. Support for Programming Languages and Operating Systems (ASPLOS-III)\fR,
  1883. pp. 54-63, April 1989.
  1884. .RE
  1885. .\" .LP
  1886. .\" [Hoare 1978]
  1887. .\" .RS
  1888. .\" C.A.R. Hoare.
  1889. .\" Communicating Sequential Processes.
  1890. .\" \fICommunications of the ACM 21\fR,8 (Aug. 1978), pp. 666-677.
  1891. .\" .RE
  1892. .LP
  1893. [Halstead 1986]
  1894. .RS
  1895. R. Halstead, Jr.
  1896. An Assessment of Multilisp: Lessons from Experience.
  1897. \fIInternational Journal of Parallel Programming 15\fR,6
  1898. (Dec. 1986).
  1899. .RE
  1900. .\" .LP
  1901. .\" [Joyce et al. 1987]
  1902. .\" .RS
  1903. .\" J. Joyce, G. Lomow, K. Slind, B. Unger.
  1904. .\" Monitoring Distributed Systems.
  1905. .\" \fIACM Transactions on Computer Systems\fR, vol. 5, no. 2, May 1987,
  1906. .\" pp. 121-150.
  1907. .\" .RE
  1908. .LP
  1909. [Kerola & Schwetman 1987]
  1910. .RS
  1911. Teemu Kerola and Herb Schwetman.
  1912. Monit:  A Performance Monitoring Tool for Parallel
  1913. and Pseudo-Parallel Programs.
  1914. \fIProc. 1987 ACM SIGMETRICS Conference\fR,
  1915. May 1987.
  1916. .RE
  1917. .LP
  1918. [Ma et al. 1987]
  1919. .RS
  1920. H.T. Ma, S. Devadas, R. Wei, and A. Sangiovanni-Vincentelli.
  1921. Logic Verification Algorithms and Their Parallel Implementation.
  1922. \fIProc. 24th Design Automation Conference\fR, July 1987, pp. 283-290.
  1923. .RE
  1924. .LP
  1925. [Malony et al. 1989]
  1926. .RS
  1927. Allen Malony, Daniel Reed, James Arendt, Ruth Aydt, Dominique Grabas,
  1928. and Brian Totty.
  1929. An Integrated Performance Data Collection, Analysis, and Visualization
  1930. System.  \fIProc. 4th Conference on Hypercubes, Concurrent
  1931. Computers, and Applications\fR, 1989.
  1932. .RE
  1933. .LP
  1934. [Miller & Yang 1987]
  1935. .RS
  1936. Barton P. Miller and C.-Q. Yang.
  1937. IPS:  An Interactive and Automatic Performance
  1938. Measurement Tool for Parallel and Distributed
  1939. Programs.
  1940. \fIProc. 7th International Conference on
  1941. Distributed Computing Systems\fR, September 1987.
  1942. .RE
  1943. .\" .LP
  1944. .\" [Miller et al. 1989]
  1945. .\" .RS
  1946. .\" B. Miller, M. Clark, S. Kierstead, S. Lim.
  1947. .\" IPS-2: The Second Generation of a Parallel Program Measurement System.
  1948. .\" Department of Computer Science, University of Wisconsin, 1989.
  1949. .\" .RE
  1950. .LP
  1951. [Moeller-Nielsen & Staunstrup 1987]
  1952. .RS
  1953. P. Moeller-Nielsen and J. Staunstrup.
  1954. Problem-Heap:  A Paradigm for Multiprocessor Algorithms.
  1955. \fIParallel Computing 4\fR, North-Holland, 1987, pp. 63-74. 
  1956. .RE
  1957. .\" .LP
  1958. .\" [Ousterhout 1984]
  1959. .\" .RS
  1960. .\" J. Ousterhout.
  1961. .\" Scheduling Techniques for Concurrent Systems.
  1962. .\" \fIProc. 3rd International Conference on Distributed Computing Systems\fR,
  1963. .\" 1984.
  1964. .\" .RE
  1965. .LP
  1966. [Pfister et al. 1985]
  1967. .RS
  1968. G. Pfister, W. Brantley, D. George, S. Harvey, W. Kleinfelder, K. McAuliffe,
  1969. E. Melton, V. Norton, and J. Weise.  The IBM Research Parallel Processor
  1970. Prototype (RP3):  Introduction and Architecture.
  1971. \fIProc. 1985 International Conference on Parallel Processing\fR,
  1972. August 1985.
  1973. .RE
  1974. .LP
  1975. [Rodgers 1986]
  1976. .RS
  1977. David P. Rodgers.  Personal communication.
  1978. .RE
  1979. .LP
  1980. [Segall & Rudolph 1985]
  1981. .RS
  1982. Zary Segall and Larry Rudolph.
  1983. PIE:  A Programming and Instrumentation Environment
  1984. for Parallel Processing.
  1985. \fIIEEE Software 2\fR,6 (November 1985).
  1986. .RE
  1987. .LP
  1988. [Sequent 1988]
  1989. .RS
  1990. Sequent Computer Systems, Inc.
  1991. Symmetry Technical Summary.
  1992. .RE
  1993. .LP
  1994. [Thacker et al. 1988]
  1995. .RS
  1996. Charles Thacker, Lawrence Stewart, and Edward Satterthwaite Jr.
  1997. Firefly:  A Multiprocessor Workstation.
  1998. \fIIEEE Transactions on Computers 37\fR,8 (Aug. 1988), pp. 909-920.
  1999. .RE
  2000. .\" .LP
  2001. .\" [Tucker & Gupta 1989]
  2002. .\" .RS
  2003. .\" A. Tucker and A. Gupta.
  2004. .\" Process Control and Scheduling Issues for Multiprogrammed Shared Memory
  2005. .\" Multiprocessors.
  2006. .\" \fIProc. 12th ACM Symposium on Operating Systems Principles\fR,
  2007. .\" December 1989.
  2008. .\" .RE
  2009. .\" .LP
  2010. .\" [Weber & Gupta 1989]
  2011. .\" .RS
  2012. .\" W. Weber and A. Gupta.
  2013. .\" Analysis of Cache Invalidation Patterns in Multiprocessors.
  2014. .\" \fIProc. Third International Conference on Architectural
  2015. .\" Support for Programming Languages and Operating Systems (ASPLOS-III)\fR,
  2016. .\" April 1989, pp. 243-255.
  2017. .\" .RE
  2018. .LP
  2019. [Yang & Miller 1988]
  2020. .RS
  2021. Cui-Qing Yang and Barton Miller.  Critical Path Analysis for the Execution
  2022. of Parallel and Distributed Programs.  \fIProc. 9th International
  2023. Conference on Distributed Computing Systems\fR, pp. 366-373, June 1988.
  2024. .RE
  2025.  
  2026. .\" .PP
  2027. .\" This
  2028. .\" accounted for less than 2% of total processor time according
  2029. .\" to gprof \- an acceptable overhead.
  2030. .\" However, Quartz showed that this logging
  2031. .\" actually accounted for 25% of the program's
  2032. .\" runtime, because it occurred during the sequential initialization
  2033. .\" phase of the program.
  2034. .\" The importance of reflecting the program
  2035. .\" structure by reporting the work done
  2036. .\" on behalf of a procedure was demonstrated here,
  2037. .\" since the time was split among several
  2038. .\" lower-level procedures called by the logging function.
  2039. .\" When logging was removed, the program's speedup
  2040. .\" improved from 9.2 to 12.2.
  2041.